﻿using System;
using System.Configuration;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace DbSync
{
    /// <summary>
    /// 日志帮助类。AppSettings节点可以配置LogTrace="off"来关闭日志记录。
    /// 如果不传入path参数，默认是在~/Log/下生成日志文件。
    /// </summary>
    public class LogHelper
    {
        private static readonly object Olock = new object();
        //读写锁，当资源处于写入模式时，其他线程写入需要等待本次写入结束之后才能继续写入
        static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
        private static int LogTaskCount { get; set; }
        private static string TraceModel { get; set; }
        public static int StackTraceType { get; set; }

        private enum LogHelperType
        {
            debug,
            error,
            all,
            sys
        }

        static LogHelper()
        {
            //配置生产环境trace=debug 记录调试信息;trace=error 记录错误信息;trace=off 关闭所有记录;默认只记录错误信息
            string trace = ConfigHelper.GetValueByKey("logLevel");
            if (string.IsNullOrEmpty(trace))
            {
                TraceModel = "error";
            }
            else
            {
                TraceModel = trace == "1" ? "debug" : "error";
            }
            //TraceModel = string.IsNullOrEmpty(trace) ? "error" : trace;
        }
        /// <summary>
        /// get Function trace
        /// </summary>
        /// <param name="deep">跟踪深度</param>
        /// <returns></returns>
        public static string GetFrame(int deep = 0)
        {
            //堆栈深度:调试+1,发布+2
            deep = deep < 0 ? StackTraceType : StackTraceType + deep;
            deep = deep + 1;//1
            System.Diagnostics.StackTrace ss = new System.Diagnostics.StackTrace(true);
            System.Diagnostics.StackFrame[] stFrames = ss.GetFrames();
            System.Reflection.MethodBase mb = stFrames[deep].GetMethod();
            if (mb.Name.Contains("Callback"))
            {
                mb = stFrames[deep + 1].GetMethod();
            }
            return mb.DeclaringType.FullName + "-->" + mb.Name;
        }

        /// <summary>
        /// 记录调试日志
        /// </summary>
        /// <Param name="content">内容。如需换行可使用：\r\n</Param>
        /// <Param name="filePrefixName"></Param>
        /// <Param name="path">格式：X:\\File\\Logs\\</Param>
        public static void Debug(string content, string filePrefixName, string traceInfo = null, string path = null)
        {
            var thistraceInfo = string.IsNullOrEmpty(traceInfo) ? GetFrame(1) : traceInfo;
            Write(LogHelperType.debug, content, filePrefixName, thistraceInfo, path);
        }
        /// <summary>
        /// 记录错误日志
        /// </summary>
        /// <Param name="content">内容。如需换行可使用：\r\n</Param>
        /// <Param name="filePrefixName"></Param>
        /// <Param name="path">格式：X:\\File\\Logs\\</Param>
        public static void Error(string content, string filePrefixName, string traceInfo = null, string path = null)
        {
            var thistraceInfo = string.IsNullOrEmpty(traceInfo) ? GetFrame(1) : traceInfo;
            Write(LogHelperType.error, content, filePrefixName, thistraceInfo, path);
        }
        /// <summary>
        /// 强制记录系统日志
        /// </summary>
        /// <Param name="content">内容。如需换行可使用：\r\n</Param>
        /// <Param name="filePrefixName"></Param>
        /// <Param name="path">格式：X:\\File\\Logs\\</Param>
        public static void Sys(string content, string filePrefixName, string traceInfo = null, string path = null)
        {
            WriteFile(LogHelperType.sys, content, filePrefixName, path);
        }
        /// <summary>
        /// filePrefixName是文件名前缀，方便在程序Logs文件下查看。
        /// </summary>
        /// <Param name="content">内容。如需换行可使用：\r\n</Param>
        /// <Param name="filePrefixName"></Param>
        /// <Param name="path"></Param>
        /// <Param name="logtype"></Param>
        private static void Write(LogHelperType logtype, string content, string filePrefixName = null, string traceInfo = null, string path = null)
        {
            string thistraceInfo = (string.IsNullOrEmpty(traceInfo) ?"LogHelper" : traceInfo) + "\r\n";
            Task.Factory.StartNew(() =>
            {
                content = thistraceInfo + content;
                try
                {
                    switch (TraceModel)
                    {
                        case "off":
                            break;
                        case "debug":
                            WriteFile(logtype, content, filePrefixName, path);
                            break;
                        case "error":
                        default:
                            if (logtype == LogHelperType.error)
                            {
                                WriteFile(logtype, content, filePrefixName, path);
                            }
                            break;
                    }
                }
                catch (Exception ex)
                {
                    //日志系统异常
                    WriteFile(LogHelperType.error, "日志系统发生异常:" + ex.Message, "loghelper", "");
                }
            });
        }

        //写入磁盘
        private static void WriteFile(LogHelperType logtype, string content, string filePrefixName = null, string path = null)
        {
            try
            {
                LogWriteLock.EnterWriteLock();
                #region 日志文件
                //默认日志文件保存名称               
                var fileName = logtype.ToString() + "_" + filePrefixName + ".txt";
                if (string.IsNullOrWhiteSpace(path))
                {
                    path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\" + DateTime.Now.ToString("yyyyMMdd") + "\\" + fileName; //项目根目录                      
                }
                else
                {
                    path += fileName;
                }
                var di = new DirectoryInfo(path.Replace(fileName, ""));
                if (!di.Exists)
                {
                    di.Create();
                }
                //判断文件大小，需要新开文件TODO
                using (var fs = new FileStream(path, FileMode.Append, FileAccess.Write))
                {
                    var sw = new StreamWriter(fs);
                    sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                    sw.WriteLine(content);
                    sw.WriteLine("-----------------------------------------------------------------------------");
                    sw.Flush();
                    sw.Close();
                }
                #endregion
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                LogWriteLock.ExitWriteLock();
            }
        }
        private static string GetMethodInfo(int deep)
        {
            deep = deep < 0 ? 0 : deep;
            System.Diagnostics.StackTrace ss = new System.Diagnostics.StackTrace(true);
            System.Diagnostics.StackFrame[] stFrames = ss.GetFrames();
            //IIS10以上版本兼容
            //if (deep > 1 && stFrames[2].GetMethod().Name == "Callback")
            //{
            //    deep += 1;
            //}
            //string tracelist = "";
            //foreach (var item in stFrames)
            //{
            //    var Method = item.GetMethod();
            //    tracelist += Method.DeclaringType.FullName + "-->" + Method.Name + "\r\n";
            //}
            //Debug(tracelist, "trackTest", "123456");
            System.Reflection.MethodBase mb = stFrames[deep].GetMethod();
            return mb.DeclaringType.FullName + "-->" + mb.Name;
        }
        /// <summary>
        /// GetMethodInfoTest
        /// </summary>
        /// <returns></returns>
        private static string GetMethodInfoTest()
        {
            string str = "";

            //取得当前方法命名空间    
            str += "命名空间名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace + "\n";

            //取得当前方法类全名 包括命名空间    
            str += "类名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "\n";

            //取得当前方法名    
            str += "方法名:" + System.Reflection.MethodBase.GetCurrentMethod().Name + "\n"; 
            str += "\n";

            //父方法
            System.Diagnostics.StackTrace ss = new System.Diagnostics.StackTrace(true);
            System.Reflection.MethodBase mb = ss.GetFrame(1).GetMethod();//1.上级,2.上上级,...

            //取得父方法命名空间    
            str += mb.DeclaringType.Namespace + "\n";

            //取得父方法类名    
            str += mb.DeclaringType.Name + "\n";

            //取得父方法类全名    
            str += mb.DeclaringType.FullName + "\n";

            //取得父方法名    
            str += mb.Name + "\n";
            return str;
        }
    }
}
